#ifndef __FUNCTIONAL_M3N_H__
#define __FUNCTIONAL_M3N_H__
/*********************************************************************
 * Software License Agreement (Modified BSD License)
 *
 * Copyright (c) 2010, Daniel Munoz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of the copyright holders' organizations nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************/

#include <vector>
#include <utility>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>

#include <m3n/m3n_model.h>
#include <m3n/random_field.h>
#include <m3n/regressors/regressor_includes.h>

// --------------------------------------------------------------
//* FunctionalM3N
/*!
 * \brief A Max-Margin Markov Network trained with Euclidean functional
 *        gradient boosting
 */
// --------------------------------------------------------------
class FunctionalM3N: public M3NModel
{
  public:
    // --------------------------------------------------------------
    /*!
     * \brief Instantiates this FunctionalM3N to use the specified
     *        regressor during boosting
     *
     * The regressor can be changed with setTemplateRegressor
     *
     * \param robust_potts_params \see M3NModel
     * \param template_regressor The regressor to implement the functional gradient
     */
    // --------------------------------------------------------------
    FunctionalM3N(const std::vector<double>& robust_potts_params,
                  const RegressorWrapper& template_regressor);

    // --------------------------------------------------------------
    /*!
     * \brief Instantiates this FunctionalM3N from the saved model
     *
     * The template regressor is the last regressor from the loaded model
     *
     * \see M3NModel::loadFromFile
     */
    // --------------------------------------------------------------
    FunctionalM3N(std::string directory,
                  std::string basename);

    // --------------------------------------------------------------
    /*!
     * \brief Destroys all allocated memory
     */
    // --------------------------------------------------------------
    ~FunctionalM3N();

    // --------------------------------------------------------------
    /*!
     * \brief Changes the regressor used to implement the functional gradient
     */
    // --------------------------------------------------------------
    void setTemplateRegressor(const RegressorWrapper& template_regressor);

    // --------------------------------------------------------------
    /*!
     * \brief Clears this FunctionalM3N so it can be trained from scratch
     *
     * The template regressor is unchanged
     */
    // --------------------------------------------------------------
    virtual void clear();

  protected:
    // --------------------------------------------------------------
    /*!
     * \see M3NModel::doSaveToFile
     */
    // --------------------------------------------------------------
    virtual int doSaveToFile(std::ofstream& outfile,
                             std::string directory,
                             std::string basename);

    // --------------------------------------------------------------
    /*!
     * \see M3NModel::doSaveToFile
     */
    // --------------------------------------------------------------
    virtual int doLoadFromFile(std::ifstream& infile,
                               std::string directory,
                               std::string basename);

    // --------------------------------------------------------------
    /*!
     * \see M3NModel::doSubgradientUpdate
     */
    // --------------------------------------------------------------
    virtual void doSubgradientUpdate(const std::vector<const RandomField*>& training_rfs,
                                     const bool infer_random,
                                     const double step_size,
                                     M3NLogger& logger);

    // --------------------------------------------------------------
    /*!
     * \see M3NModel::computePotential
     */
    // --------------------------------------------------------------
    virtual int computePotential(const RandomField::Node& node,
                                 const unsigned int label,
                                 const size_t max_nbr_regressors,
                                 double& potential_val) const;

    // --------------------------------------------------------------
    /*!
     * \see M3NModel::computePotential
     */
    // --------------------------------------------------------------
    virtual int computePotential(const RandomField::Clique& clique,
                                 const unsigned int clique_set_idx,
                                 const unsigned int label,
                                 const size_t max_nbr_regressors,
                                 double& potential_val) const;
  private:
    // --------------------------------------------------------------
    /*!
     * \brief Truncates the given value between [-1,1]
     *
     * \param value The value to truncate
     */
    // --------------------------------------------------------------
    void truncateUnit(double& value) const;

    // --------------------------------------------------------------
    /*!
     * \brief Instantiates the specified RegressorWrapper with default
     *        parameters
     *
     * \param regressor_algorithm The regressor algorithm to use
     *
     * \return the instantiated RegressorWrapper on success, otherwise NULL on error
     */
    // --------------------------------------------------------------
    RegressorWrapper* instantiateRegressor(const RegressorWrapper::algorithm_t regressor_algorithm);

    /*! \brief All instantiated regressors are duplicates of this one */
    RegressorWrapper* m_template_regressor;

    /*! \brief The weak learners */
    std::vector<std::pair<double, RegressorWrapper*> > m_regressors;
};
#endif
